package com.ideaplugin.datamanager.table.data.simple.service.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.PageUtil;
import com.base.param.*;
import com.base.sqlparser.SqlCreator;
import com.ideaplugin.datamanager.common.CommonValidEnum;
import com.ideaplugin.datamanager.common.DimensionTypeColumn;
import com.ideaplugin.datamanager.entity.TableInfo;
import com.ideaplugin.datamanager.manager.TableInfoManager;
import com.ideaplugin.datamanager.model.common.PageRequest;
import com.ideaplugin.datamanager.model.common.PageResult;
import com.ideaplugin.datamanager.model.request.DeleteUserTableDataParam;
import com.ideaplugin.datamanager.model.request.SaveUserTableDataParam;
import com.ideaplugin.datamanager.model.request.UserTableDataParam;
import com.ideaplugin.datamanager.model.response.PageUserTableDataVo;
import com.ideaplugin.datamanager.model.response.TableColumnVo;
import com.ideaplugin.datamanager.model.response.UserAuthColumnVo;
import com.ideaplugin.datamanager.model.response.UserTableDataVo;
import com.ideaplugin.datamanager.service.UserTableService;
import com.ideaplugin.datamanager.table.data.simple.service.SimpleUserTableDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.ideaplugin.datamanager.entity.table.TableInfoTableDef.TABLE_INFO;

@Service
public class SimpleUserTableDataServiceImpl implements SimpleUserTableDataService {
    @Autowired
    private UserTableService userTableService;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private SqlCreator sqlCreator;
    @Autowired
    private TableInfoManager tableInfoManager;

    @Override
    public PageResult<PageUserTableDataVo> pageUserTableData(PageRequest<UserTableDataParam> pageRequest) {
        PageResult<PageUserTableDataVo> result = new PageResult<>();
        final UserTableDataParam queryParam = pageRequest.getQuery();
        final Long tableInfoId = queryParam.getTableInfoId();
        final UserAuthColumnVo userAuthColumn = userTableService.getUserAuthColumn(tableInfoId);
        if (Objects.isNull(userAuthColumn) || CollUtil.isEmpty(userAuthColumn.getAuthColumns())) {
            return result;
        }
        final Map<String, TableColumnVo> authTableColumnVoMap = userAuthColumn.getAuthColumns().stream()
                .collect(Collectors.toMap(TableColumnVo::getColumnCode, Function.identity()));

        final SelectSqlParam.SelectItem selectItem = getSelectParam(userAuthColumn);
        if (CollUtil.isEmpty(selectItem.getSelectColumns())) {
            return result;
        }
        final SelectSqlParam selectSqlParam = new SelectSqlParam(getTableInfo(tableInfoId).getTableName())
                .setWhereParams(filterWhereParam(queryParam.getWhereParams(), authTableColumnVoMap))
                .setLimitParam(getLimitParam(pageRequest))
                .setSelectItem(selectItem);
        final String countSql = sqlCreator.getCountSql(selectSqlParam);
        Long totalSize = jdbcTemplate.queryForObject(countSql, Long.class);
        if (totalSize < 1) {
            return result;
        }
        result.setTotal(totalSize);
        final String simpleSelectSql = sqlCreator.getSimpleSelectSql(selectSqlParam);
        final List<Map<String, Object>> dataMap = jdbcTemplate.queryForList(simpleSelectSql);
        final List<PageUserTableDataVo> resultData = dataMap.stream().map(data -> {
            PageUserTableDataVo pageUserTableDataVo = new PageUserTableDataVo();
            List<UserTableDataVo> tableData = new ArrayList<>();
            data.forEach((key, value) -> {
                tableData.add(new UserTableDataVo(authTableColumnVoMap.get(key).getColumnName(), key, value));
            });
            pageUserTableDataVo.setTableData(tableData);
            return pageUserTableDataVo;
        }).collect(Collectors.toList());
        result.setData(resultData);
        result.setPage((long) pageRequest.getPage().getPageIndex());
        result.setPageSize(pageRequest.getPage().getPageSize());
        return result;
    }


    @Override
    public Boolean saveUserTableData(SaveUserTableDataParam param) {
        final TableInfo tableInfo = tableInfoManager.getById(param.getTableInfoId());
        final List<ColumnValueParam> whereColumnValueParams = param.getWhereColumnValueParams();
        String sql;
        if (CollUtil.isEmpty(whereColumnValueParams)) {
            final InsertSqlParam insertSqlParam = new InsertSqlParam(tableInfo.getTableName());
            insertSqlParam.setColumnValueParams(param.getColumnValueParams());
            insertSqlParam.setValueIsPlaceholder(false);
            sql = sqlCreator.getSimpleInsertSql(insertSqlParam);
        } else {
            final UpdateSqlParam updateSqlParam = new UpdateSqlParam(tableInfo.getTableName());
            updateSqlParam.setValueIsPlaceholder(false);
            final Map<String, ColumnValueParam> whereValueParamMap = whereColumnValueParams.stream()
                    .collect(Collectors.toMap(ColumnValueParam::getColumnCode, Function.identity()));
            final List<ColumnValueParam> filterColumnValue = param.getColumnValueParams()
                    .stream().filter(col -> !whereValueParamMap.containsKey(col.getColumnCode()))
                    .collect(Collectors.toList());
            updateSqlParam.setColumnValueParams(filterColumnValue);

            final List<WhereParam> whereParams = whereColumnValueParams.stream()
                    .map(valueParam -> new WhereParam(valueParam.getColumnCode(), SqlKeyword.EQ, valueParam.getValue()))
                    .collect(Collectors.toList());
            updateSqlParam.setWhereParams(whereParams);
            sql = sqlCreator.getSimpleUpdateSql(updateSqlParam);
        }
        final int update = jdbcTemplate.update(sql);
        return update > 0;
    }

    private static SelectSqlParam.SelectItem getSelectParam(UserAuthColumnVo userAuthColumn) {
        final List<String> filterSelectColmun = userAuthColumn.getAuthColumns()
                .stream()
                .filter(vo -> Objects.equals(vo.getShow(), CommonValidEnum.valid.getValue())
                        && !Objects.equals(vo.getDimensionType(), DimensionTypeColumn.agg.name()))
                .map(TableColumnVo::getColumnCode)
                .collect(Collectors.toList());
        return new SelectSqlParam.SelectItem().withSelectColumns(filterSelectColmun);
    }

    private TableInfo getTableInfo(Long tableInfoId) {
        final TableInfo tableInfo = tableInfoManager.queryChain()
                .from(TableInfo.class)
                .select(TABLE_INFO.TABLE_NAME)
                .where(TABLE_INFO.ID.eq(tableInfoId))
                .one();
        return tableInfo;
    }

    private static SelectSqlParam.LimitParam getLimitParam(PageRequest<UserTableDataParam> pageRequest) {
        PageUtil.setFirstPageNo(1);
        final int[] pages = PageUtil.transToStartEnd(pageRequest.getPage().getPageIndex(), pageRequest.getPage().getPageSize());
        final SelectSqlParam.LimitParam limitParam = new SelectSqlParam.LimitParam(pages[0], pages[1]);
        return limitParam;
    }

    /**
     * 只能使用有权限的字段
     *
     * @param whereParams
     * @param authTableColumnVoMap
     * @return
     */
    private static List<WhereParam> filterWhereParam(List<WhereParam> whereParams, Map<String, TableColumnVo> authTableColumnVoMap) {
        if (CollUtil.isEmpty(whereParams)) {
            return List.of();
        }
        return whereParams
                .stream()
                .filter(whereParam -> authTableColumnVoMap.containsKey(whereParam.getColumnCode()))
                .collect(Collectors.toList());
    }


    @Override
    public List<UserTableDataVo> queryAggregate(UserTableDataParam param) {
        final Long tableInfoId = param.getTableInfoId();
        final UserAuthColumnVo userAuthColumn = userTableService.getUserAuthColumn(tableInfoId);
        if (Objects.isNull(userAuthColumn) || CollUtil.isEmpty(userAuthColumn.getAuthColumns())) {
            return List.of();
        }
        final Map<String, TableColumnVo> authTableColumnVoMap = userAuthColumn.getAuthColumns().stream()
                .collect(Collectors.toMap(TableColumnVo::getColumnCode, Function.identity()));

        final List<String> aggSelectColumnCodes = userAuthColumn.getAuthColumns()
                .stream()
                .filter(vo -> Objects.equals(vo.getDimensionType(), DimensionTypeColumn.agg.name()))
                .map(TableColumnVo::getColumnCode)
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(aggSelectColumnCodes)) {
            return List.of();
        }
        final SelectSqlParam.SelectItem selectItem = getSelectItem(aggSelectColumnCodes, authTableColumnVoMap);

        final SelectSqlParam selectSqlParam = new SelectSqlParam(getTableInfo(tableInfoId).getTableName())
                .setWhereParams(filterWhereParam(param.getWhereParams(), authTableColumnVoMap))
                .setSelectItem(selectItem);
        final String simpleSelectSql = sqlCreator.getSimpleSelectSql(selectSqlParam);
        final List<Map<String, Object>> dataMap = jdbcTemplate.queryForList(simpleSelectSql);
        final Map<String, Object> queryResult = dataMap.get(0);
        List<UserTableDataVo> result = new ArrayList<>();
        for (Map.Entry<String, Object> entry : queryResult.entrySet()) {
            final String key = entry.getKey();
            final Object value = entry.getValue();
            result.add(new UserTableDataVo(authTableColumnVoMap.get(key).getColumnName(), key, value));
        }
        return result;
    }

    private static SelectSqlParam.SelectItem getSelectItem(List<String> aggSelectColumnCodes, Map<String, TableColumnVo> authTableColumnVoMap) {
        final SelectSqlParam.SelectItem selectItem = new SelectSqlParam.SelectItem();
        final List<SelectItemParam> selectItemParams = aggSelectColumnCodes.stream().map(columnCode -> {
            SelectItemParam itemParam = new SelectItemParam();
            itemParam.setColumnCode(columnCode.split(StrPool.AT)[1]);
            final String functionName = authTableColumnVoMap.get(columnCode).getDimensionExtra();
            itemParam.setFunctionName(SqlFunctionKeyword.valueOf(functionName));
            itemParam.setAlias(columnCode);
            return itemParam;
        }).collect(Collectors.toList());
        selectItem.setSelectColumns(selectItemParams);
        return selectItem;
    }

    @Override
    public Boolean deleteUserTableData(DeleteUserTableDataParam param) {
        final TableInfo tableInfo = tableInfoManager.getById(param.getTableInfoId());
        final List<ColumnValueParam> whereColumnValueParams = param.getWhereColumnValueParams();
        final List<WhereParam> whereParams = whereColumnValueParams.stream().map(columnValueParam -> {
            final String columnCode = columnValueParam.getColumnCode();
            final Object columnValue = columnValueParam.getValue();
            return new WhereParam(columnCode, SqlKeyword.EQ, columnValue);
        }).collect(Collectors.toList());
        final DeleteSqlParam deleteSqlParam = new DeleteSqlParam(tableInfo.getTableName());
        deleteSqlParam.setWhereParams(whereParams);
        final String deleteSql = sqlCreator.getSimpleDeleteSql(deleteSqlParam);
        return jdbcTemplate.update(deleteSql) > 0;
    }
}

